/*
 * 
 * 
 */
package de.hpi.generator.helpers;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;

/**
 *
 * @author Michael Wolowyk
 */
public class LargeDataOfIntegersStorage {

    protected final int PAGE_SIZE_IN_BYTES = 10485760; // 10 mbytes
    private final int ITEM_SIZE_IN_BYTES = 4;
    protected final int MAX_ITEMS_COUNT = (PAGE_SIZE_IN_BYTES / ITEM_SIZE_IN_BYTES);

    protected ByteBuffer aCacheWrapper = null;
    protected byte aCache[] = null;
    private String aFolderForDataFiles = null;
    protected int aCurrentPageIndex = -1;
    private boolean aIsEmpty = true;

    public LargeDataOfIntegersStorage(String aFolderForDataFiles) {
        this.aFolderForDataFiles = aFolderForDataFiles;
    }

    public void clean() throws Exception {
        if (new File(aFolderForDataFiles).exists()) {
            FileHelper.removeFileOrDirectory(aFolderForDataFiles);
        }

        aCacheWrapper = null;
        aCache = null;
        aCurrentPageIndex = -1;
        aIsEmpty = true;
    }

    public int getValueByIndex(long aValueIndex) throws Exception {
        int aOffsetInBytes = loadPageForValue(aValueIndex);
        return aCacheWrapper.getInt(aOffsetInBytes);
    }
    
    public int getItemSizeInBytes(){
        return ITEM_SIZE_IN_BYTES;
    }

    public void setValueByIndex(int aValue, long aValueIndex) throws Exception {
        int aOffsetInBytes = loadPageForValue(aValueIndex);
        aCacheWrapper.putInt(aOffsetInBytes, aValue);
        aIsEmpty = false;
    }

    public boolean isEmpty() {
        return aIsEmpty;
    }

    private int loadPageForValue(long aValueIndex) throws Exception {
        swapToPage(getPageIndexFromValueIndex(aValueIndex));
        return getOffsetInPageInBytes(aValueIndex);
    }

    private int getPageIndexFromValueIndex(long aValueIndex) {
        long result = aValueIndex / MAX_ITEMS_COUNT;
        return (int) result;
    }

    private void swapToPage(int aPageIndex) throws Exception {
        if (aPageIndex != aCurrentPageIndex) {
            flushCacheIntoPage(aCurrentPageIndex);
            cleanCache();
            loadPageIntoCache(aPageIndex);
            
            aCurrentPageIndex = aPageIndex;
        }
    }

    private void flushCacheIntoPage(int aPageIndex) throws Exception {
        if(aCache == null) return;
        
        File aFileForPage = getFileForPage(aPageIndex);
        FileHelper.recreateFile(aFileForPage.getAbsolutePath());
        
        BufferedOutputStream aOutStream = new BufferedOutputStream(new FileOutputStream(aFileForPage), PAGE_SIZE_IN_BYTES);
        aOutStream.write(aCache);

        aOutStream.close();
    }

    private void cleanCache() {
        aCacheWrapper = null;
        aCache = null;
        aCache = new byte[PAGE_SIZE_IN_BYTES];
        aCacheWrapper = ByteBuffer.wrap(aCache);
    }

    private void loadPageIntoCache(int aPageIndex) throws Exception {
        File aFileForPage = getFileForPage(aPageIndex);

        if (aFileForPage.exists()) {
            BufferedInputStream aInStream = new BufferedInputStream(new FileInputStream(aFileForPage), PAGE_SIZE_IN_BYTES);
            aInStream.read(aCache);

            aInStream.close();
        }
    }

    private File getFileForPage(int aCurrentPageIndex) throws Exception {
        File result = new File(String.format("%s\\PageNo%d.bin", aFolderForDataFiles, aCurrentPageIndex));
        return result;
    }

    private int getOffsetInPageInBytes(long aValueIndex) {
        long result = aValueIndex % MAX_ITEMS_COUNT;
        return (int) result *  ITEM_SIZE_IN_BYTES;
    }
}
